// Vahe and Arman
/**
* Copyright (C) 2008 by The Regents of the University of California
* Redistribution of this file is permitted under the terms of the GNU
* Public License (GPL).
*
* @author Junghoo "John" Cho <cho AT cs.ucla.edu>
* @date 3/24/2008
*/

#include <cstdio>
#include <iostream>
#include <fstream>
#include "Bruinbase.h"
#include "SqlEngine.h"
#include "BTreeIndex.h"

using namespace std;

// external functions and variables for load file and sql command parsing
extern FILE* sqlin;
int sqlparse(void);


RC SqlEngine::run(FILE* commandline)
{
  fprintf(stdout, "Bruinbase> ");

  // set the command line input and start parsing user input
  sqlin = commandline;
  sqlparse(); // sqlparse() is defined in SqlParser.tab.c generated from
               // SqlParser.y by bison (bison is GNU equivalent of yacc)

  return 0;
}

RC SqlEngine::select(int attr, const string& table, const vector<SelCond>& cond)
{
  RecordFile RF;
  RecordId RID;
  BTreeIndex index;
  IndexCursor cursor;
  RC rc;
  string value;
  int count, diff, lookup, key;

  if ((rc =RF.open(table + ".tbl", 'r')) < 0) 
  {
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }

  if (attr == 4) goto elseCond;
  if (!index.open(table+".idx", 'r'))
  {
    lookup = -1;
    for (unsigned i = 0; i < cond.size(); i++)
    {
/*	  if (cond[i].comp == SelCond::EQ) { cout << "EQ" << "    value = " ; printf( "%s \n", cond[i].value); }
	  if (cond[i].comp == SelCond::NE) { cout << "NE" << "    value = " ; printf( "%s \n", cond[i].value); }	
      if (cond[i].comp == SelCond::LT) { cout << "LT" << "    value = " ; printf( "%s \n", cond[i].value); }	
      if (cond[i].comp == SelCond::GT) { cout << "GT" << "    value = " ; printf( "%s \n", cond[i].value); }	
      if (cond[i].comp == SelCond::LE) { cout << "LE" << "    value = " ; printf( "%s \n", cond[i].value); }	
      if (cond[i].comp == SelCond::GE) { cout << "GE" << "    value = " ; printf( "%s \n", cond[i].value); }		  
 */ 
      if (cond[i].attr != 1) continue;
      if (cond[i].comp == SelCond::EQ) { lookup = i; break; }
     
      if (cond[i].comp == SelCond::GT || cond[i].comp == SelCond::GE)
        if (lookup == -1 || atoi(cond[i].value) > atoi(cond[lookup].value)) lookup = i;
	}

	if (lookup > -1) index.locate(atoi(cond[lookup].value), cursor);
    else index.locate(0,cursor);

    count = 0;
    nextItr:
	while (!index.readForward(cursor, key, RID))
    {
      if ((rc =RF.read(RID, key, value)) < 0) 
	  {
        fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
        goto exit_select;
      }

      for (unsigned i = 0; i < cond.size(); i++)
      {
        switch (cond[i].attr)
        {
        case 1:
          diff = key - atoi(cond[i].value);
          break;
        case 2:
          diff = strcmp(value.c_str(), cond[i].value);
          break;
        }

        switch (cond[i].comp)
        {
        case SelCond::EQ:
          if (diff != 0)
            if (cond[i].attr == 1) goto finish_read;
            else goto nextItr;
          break;
        case SelCond::NE:
          if (diff == 0) goto nextItr;
          break;
        case SelCond::GT:
          if (diff <= 0) goto nextItr;
          break;
        case SelCond::LT:
          if (diff >= 0)
            if (cond[i].attr == 1) goto finish_read;
            else goto nextItr;
          break;
        case SelCond::GE:
          if (diff < 0) goto nextItr;
          break;
        case SelCond::LE:
          if (diff > 0)
            if (cond[i].attr == 1) goto finish_read;
            else goto nextItr;
          break;
        }
      }
      // Tuple matches conditions. Increment count
      count++;
      // print the tuple
      switch (attr) 
	  {
      case 1: // SELECT key
        fprintf(stdout, "%d\n", key);
        break;
      case 2: // SELECT value
        fprintf(stdout, "%s\n", value.c_str());
        break;
      case 3: // SELECT *
        fprintf(stdout, "%d '%s'\n", key, value.c_str());
        break;
      }
    }
  }
  
  else  // No index
  {
	elseCond:
    RID.pid = RID.sid = 0;
    count = 0;

    while (RID <RF.endRid()) 
	{
      if ((rc =RF.read(RID, key, value)) < 0) 
	  {
        fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
        goto exit_select;
      }

      for (unsigned i = 0; i < cond.size(); i++) 
	  {
        switch (cond[i].attr)
		{
        case 1:
			diff = key - atoi(cond[i].value);
			break;
        case 2:
			diff = strcmp(value.c_str(), cond[i].value);
			break;
        }

        switch (cond[i].comp)
		{
        case SelCond::EQ:
			if (diff != 0) goto next_tuple;
			break;
        case SelCond::NE:
			if (diff == 0) goto next_tuple;
			break;
        case SelCond::GT:
			if (diff <= 0) goto next_tuple;
			break;
        case SelCond::LT:
			if (diff >= 0) goto next_tuple;
			break;
        case SelCond::GE:
			if (diff < 0) goto next_tuple;
			break;
        case SelCond::LE:
			if (diff > 0) goto next_tuple;
			break;
        }
      }

      count++;
      // print the tuple
      switch (attr) 
	  {
      case 1: // SELECT key
        fprintf(stdout, "%d\n", key);
        break;
      case 2: // SELECT value
        fprintf(stdout, "%s\n", value.c_str());
        break;
      case 3: // SELECT *
        fprintf(stdout, "%d '%s'\n", key, value.c_str());
        break;
      }

      next_tuple:
      ++RID;
    }
  }

  finish_read:
  // print matching tuple count if "select count(*)"
  if (attr == 4) fprintf(stdout, "%d\n", count);
 
  rc = 0;
  exit_select:
  index.close();
 RF.close();
  return rc;
}

RC SqlEngine::load(const string& table, const string& loadfile, bool index)
{

  BTreeIndex bIndex;
  RecordFile RF;
  ifstream IS;
  string line;

  if (RF.open(table + ".tbl", 'w')) {
    cout << "Error: Could Not Access File" << endl;
    return 1;
  }

  IS.open(loadfile.c_str());
  if (!IS.is_open()) {
    cout << "Error: Could Not Access file" << endl;
    return 1;
  }

  if (index)
    if(bIndex.open(table + ".idx", 'w'))
    {
      cout << "Error: Could Not Access Index" << endl;
      return 1;
    }

  while(IS.good()) 
  {
    int key;
    string val;
    RecordId temp;
    getline(IS,line);

    if (parseLoadLine(line, key, val)) continue;
    if (RF.append(key, val, temp)) 
	{
      cout << "Warning: Could not add line to RecordFile" << endl;
      continue;
    }
    if (index)
      if(bIndex.insert(key, temp))
      {
        cout << "Warning: Could not insert key into index" << endl;
        continue;
      }
  } // end of while

  RF.close();
  IS.close();
  if (index) bIndex.close();
  return 0;
}

RC SqlEngine::parseLoadLine(const string& line, int& key, string& value)
{
    const char *s;
    char c;
    string::size_type loc;
    
    // ignore beginning white spaces
    c = *(s = line.c_str());
    while (c == ' ' || c == '\t') { c = *++s; }

    // get the integer key value
    key = atoi(s);

    // look for comma
    s = strchr(s, ',');
    if (s == NULL) { return RC_INVALID_FILE_FORMAT; }

    // ignore white spaces
    do { c = *++s; } while (c == ' ' || c == '\t');
    
    // if there is nothing left, set the value to empty string
    if (c == 0) {
        value.erase();
        return 0;
    }

    // is the value field delimited by ' or "?
    if (c == '\'' || c == '"') {
        s++;
    } else {
        c = '\n';
    }

    // get the value string
    value.assign(s);
    loc = value.find(c, 0);
    if (loc != string::npos) { value.erase(loc); }

    return 0;

}
